package xxl.robotlegs.extensions.viewmanager.impl 
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.utils.Dictionary;
	import xxl.signals.api.IMapSignal;
	import xxl.signals.api.ISignal;
	import xxl.signals.impl.Signal;
	/**
	 * ...
	 * @author XXL
	 */
	public class ContainerRegistry 
	{
		public const signal:IMapSignal = new ContainerRegistrySignal();
		private const _bindings:Vector.<ContainerBinding> = new Vector.<ContainerBinding>;
		private const _rootBindings:Vector.<ContainerBinding> = new Vector.<ContainerBinding>;
		private const _bindingByContainer:Dictionary = new Dictionary();
		
		public function ContainerRegistry() 
		{
			
		}
		
		public function get bindings():Vector.<ContainerBinding> 
		{
			return _bindings;
		}
		
		public function get rootBindings():Vector.<ContainerBinding> 
		{
			return _rootBindings;
		}
		
		public function addContainer(container:DisplayObjectContainer):ContainerBinding
		{
			return _bindingByContainer[container] ||= createBinding(container);
		}
		
		public function removeContainer(container:DisplayObjectContainer):ContainerBinding
		{
			const binding:ContainerBinding = _bindingByContainer[container];
			binding && removeBinding(binding);
			return binding;
		}
		
		public function findParentBinding(target:DisplayObject):ContainerBinding
		{
			var parent:DisplayObjectContainer = target.parent;
			while (parent)
			{
				var binding:ContainerBinding = _bindingByContainer[parent];
				if (binding)
				{
					return binding;
				}
				parent = parent.parent;
			}
			return null;
		}
		
		[Inline]
		final public function getBinding(container:DisplayObjectContainer):ContainerBinding
		{
			return _bindingByContainer[container];
		}
		
		//----------private
		
		private function createBinding(container:DisplayObjectContainer):ContainerBinding
		{
			const binding:ContainerBinding = new ContainerBinding(container);
			_bindings.push(binding);
			binding.bindingEmptySignal.addOnce(onBindingEmpty);
			binding.parent = findParentBinding(container);
			if (binding.parent == null)
			{
				addRootBinding(binding);
			}
			for each(var childBinding:ContainerBinding in _bindingByContainer)
			{
				if (container.contains(childBinding.container))
				{
					if (!childBinding.parent)
					{
						removeRootBinding(childBinding);
						childBinding.parent = binding;
					}
					else if (!container.contains(childBinding.parent.container))
					{
						childBinding.parent = binding;
					}
				}
			}
			signal.dispatch(ContainerRegistrySignal.CONTAINER_ADD, binding.container);
			return binding;
		}
		
		private function removeBinding(binding:ContainerBinding):void
		{
			delete _bindingByContainer[binding.container];
			const index:int = _bindings.indexOf(binding);
			if (index >= 0)
			{
				_bindings.splice(index, 1);
			}
			binding.bindingEmptySignal.remove(onBindingEmpty);
			if (!binding.parent)
			{
				removeRootBinding(binding);
			}
			
			for each(var childBinding:ContainerBinding in _bindingByContainer)
			{
				if (childBinding.parent == binding)
				{
					childBinding.parent = binding.parent;
					if (!childBinding.parent)
					{
						addRootBinding(childBinding);
					}
				}
			}
			
			signal.dispatch(ContainerRegistrySignal.CONTAINER_REMOVE, binding.container);
		}
		
		private function addRootBinding(binding:ContainerBinding):void
		{
			_rootBindings.push(binding);
			signal.dispatch(ContainerRegistrySignal.ROOT_CONTAINER_ADD, binding.container);
		}
		
		private function removeRootBinding(binding:ContainerBinding):void
		{
			const index:int = _rootBindings.indexOf(binding);
			if (index >= 0)
			{
				_rootBindings.splice(index, 1);
				signal.dispatch(ContainerRegistrySignal.ROOT_CONTAINER_REMOVE, binding.container);
			}
		}
		
		private function onBindingEmpty(binding:ContainerBinding):void 
		{
			removeBinding(binding);
		}
		
	}

}